using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using SpaceShooter.Utilities;


namespace SpaceShooter.Components
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class EffectManagerComponent : Microsoft.Xna.Framework.GameComponent
    {
        public static StateManagement.GameState gs;
        static PlayerComponent player;

        public EffectManagerComponent(Game game)
            : base(game)
        {
            gs = (StateManagement.GameState)Game.Services.GetService(typeof(StateManagement.GameState));
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            player = (PlayerComponent)Game.Services.GetService(typeof(PlayerComponent));
            base.Initialize();
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            if (!gs.pause)
            {
                UpdateRocks();
                UpdateSmokes();
                UpdateExplosions();
            }
            base.Update(gameTime);
        }

        /// <summary>
        /// Handler per l'evento onNewRock lanciato da AsteroidManagerComponent
        /// </summary>
        /// <param name="Position">La posizione sullo schermo in cui deve essere visualizzato l'effetto</param>
        public void OnNewRock(Vector2 Position, Vector2 Direction){
            if(Utility.IsInScreenSpace(Utility.newRectangleFromCenterPosition(Position, 20, 20))){
                gs.rocks.AddFirst(new Rock(Position, Direction));
            }
        }

        /// <summary>
        /// Handler per l'evento onNewSmoke lanciato da EnemyManagerComponent
        /// </summary>
        /// <param name="Position">La posizione sullo schermo in cui deve essere visualizzato il fumo</param>
        public void OnNewSmoke(Vector2 Position)
        {
            if (Utility.IsInScreenSpace(Utility.newRectangleFromCenterPosition(Position, 20, 20)))
                gs.smokes.AddFirst(new Smoke(Position));
        }

        /// <summary>
        /// Handler per l'evento onNewExplosion lanciato da EnemyManagerComponent
        /// </summary>
        /// <param name="Position">La posizione sullo schermo in cui deve essere visualizzata l'esplosione</param>
        public void OnNewExplosion(Vector2 Position)
        {
            if (Utility.IsInScreenSpace(Utility.newRectangleFromCenterPosition(Position, 20, 20)))
                gs.explosions.AddFirst(new Explosion(Position));
        }

        /// <summary>
        /// Aggiorna tutte le istanze della classe Smoke presenti nella lista
        /// Ed elimina quelle che hanno terminato il loro compito
        /// </summary>
        public void UpdateSmokes()
        {
            Smoke[] smoketemp = new Smoke[gs.smokes.Count];
            int i = 0;

            //Aggiorna tutte le istanze della classe Smoke presenti nella lista
            foreach (Smoke x in gs.smokes)
            {
                if (!gs.timeTraveling)
                    x.Update();
                else
                    x.TimeTravel();

                if (x.delete)
                {
                    smoketemp[i] = x;
                    i++;
                }
            }

            //Elimina le istanze della classe Smoke che hanno terminato il loro ciclo di vita
            for (i = 0; i < smoketemp.Length && smoketemp[i]!=null; i++)
            {
                gs.smokes.Remove(smoketemp[i]);
            }
        }

        /// <summary>
        /// Aggiorna tutte le istanze della classe Rock presenti nella lista
        /// Ed elimina quelle che hanno terminato il loro compito
        /// </summary>
        public void UpdateRocks()
        {
            Rock[] rocktemp = new Rock[gs.rocks.Count];
            int i = 0;

            //Aggiorna tutte le istanze della classe Rock presenti nella lista
            foreach (Rock x in gs.rocks)
            {
                if (!gs.timeTraveling)
                    x.Update();
                else
                    x.TimeTravel();

                if (x.delete)
                {
                    rocktemp[i] = x;
                    i++;
                }
            }

            //Elimina le istanze della classe rock che hanno terminato il loro ciclo di vita
            for (i = 0; i < rocktemp.Length && rocktemp[i]!=null; i++)
            {
                gs.rocks.Remove(rocktemp[i]);
            }
        }

        /// <summary>
        /// Aggiorna tutte le istanze della classe Explosion presenti nella lista
        /// Ed elimina quelle che hanno terminato il loro compito
        /// </summary>
        public void UpdateExplosions()
        {
            Explosion[] explosiontemp = new Explosion[gs.explosions.Count];
            int i = 0;

            //Aggiorna tutte le istanze della classe Explosion presenti nella lista
            foreach (Explosion x in gs.explosions)
            {
                if (!gs.timeTraveling)
                    x.Update();
                else
                    x.TimeTravel();

                if (x.delete)
                {
                    explosiontemp[i] = x;
                    i++;
                }
            }

            //Elimina le istanze della classe rock che hanno terminato il loro ciclo di vita
            for (i = 0; i < explosiontemp.Length && explosiontemp[i]!=null; i++)
            {
                gs.explosions.Remove(explosiontemp[i]);
            }
        }

        #region Smoke
        /// <summary>
        /// Definisce un singolo oggetto fumo.
        /// </summary>
        public class Smoke
        {
            private LinkedList<SmokeState> states;
            public int deathCounter;
            public Vector2 pos;
            public bool delete;
            public int SmokeTime;
            public LifeState lifeState;
            public Vector2 Position
            {
                get
                {
                    return pos;
                }
            }

            public LinkedList<SmokeState> GetStates()
            {
                return states;
            }
            public void SetStates(SmokeState[] newStates)
            {
                states = new LinkedList<SmokeState>(newStates);
            }

            public Smoke() { }

            /// <summary>
            /// Istanzia un oggetto Smoke con posizione Position
            /// </summary>
            /// <param name="Position">La posizione del fumo sullo schermo</param>
            public Smoke(Vector2 Position)
            {
                pos = Position;
                SmokeTime = 30;
                delete = false;
                lifeState = LifeState.NORMAL;
                states = new LinkedList<SmokeState>();
                deathCounter = 0;
            }

            /// <summary>
            /// Aggiorna lo stato del fumo.
            /// </summary>
            public void Update()
            {
                if (lifeState != LifeState.DEAD)
                {
                    if (SmokeTime < 0)
                    {
                        lifeState = LifeState.DEAD;
                    }
                    else
                    {
                        SmokeTime--;
                    }

                    states.AddFirst(new SmokeState(new Vector2(Position.X, Position.Y), SmokeTime, lifeState));
                    if (states.Count > gs.timeTank + 1)
                    {
                        states.RemoveLast();
                    }
                }
                else
                {
                    if (states.Count > 0)
                    {
                        if (states.Count + deathCounter > gs.timeTank)
                            states.RemoveLast();
                    }
                    else
                    {
                        delete = true;
                    }
                    deathCounter++;
                }
            }

            public void TimeTravel()
            {
                if (states.Count > 0)
                {
                    if (deathCounter > 0)
                        deathCounter--;
                    else
                    {
                        SmokeState st = states.First.Value;
                        pos = st.pos;
                        SmokeTime = st.smokeTime;
                        lifeState = st.lifeState;
                        states.RemoveFirst();
                    }
                }
                else
                {
                    delete = true;
                }
            }
        }
        #endregion

        #region SmokeState
        public class SmokeState
        {
            public Vector2 pos;
            public int smokeTime;
            public LifeState lifeState;

            public SmokeState() { }

            public SmokeState(Vector2 Position, int SmokeTime, LifeState LifeState)
            {
                pos = Position;
                smokeTime = SmokeTime;
                lifeState = LifeState;
            }
        }
        #endregion

        #region Explosion
        /// <summary>
        /// Definisce una singola esplosione
        /// </summary>
        public class Explosion
        {
            private LinkedList<ExplosionState> states;
            public int deathCounter;
            public Vector2 pos;
            public bool delete;
            public int ExplosionTime;
            public LifeState lifeState;
            public Vector2 Position
            {
                get
                {
                    return pos;
                }
            }

            public LinkedList<ExplosionState> GetStates()
            {
                return states;
            }
            public void SetStates(ExplosionState[] newStates)
            {
                states = new LinkedList<ExplosionState>(newStates);
            }

            public Explosion() { }

            /// <summary>
            /// Istanzia un oggetto Smoke con posizione Position
            /// </summary>
            /// <param name="Position">La posizione del fumo sullo schermo</param>
            public Explosion(Vector2 Position)
            {
                pos = Position;
                ExplosionTime = 30;
                delete = false;
                lifeState = LifeState.NORMAL;
                states = new LinkedList<ExplosionState>();
                deathCounter = 0;
            }

            /// <summary>
            /// Aggiorna lo stato del fumo.
            /// </summary>
            public void Update()
            {
                if (lifeState != LifeState.DEAD)
                {
                    if (ExplosionTime < 0)
                    {
                        lifeState = LifeState.DEAD;
                    }
                    else
                    {
                        ExplosionTime--;
                    }

                    states.AddFirst(new ExplosionState(new Vector2(Position.X, Position.Y), ExplosionTime, lifeState));
                    if (states.Count > gs.timeTank + 1)
                    {
                        states.RemoveLast();
                    }
                }
                else
                {
                    if (states.Count > 0)
                    {
                        if (states.Count + deathCounter > gs.timeTank)
                            states.RemoveLast();
                    }
                    else
                    {
                        delete = true;
                    }
                    deathCounter++;
                }
            }

            public void TimeTravel()
            {
                if (states.Count > 0)
                {
                    if (deathCounter > 0)
                        deathCounter--;
                    else
                    {
                        ExplosionState st = states.First.Value;
                        pos = st.pos;
                        ExplosionTime = st.smokeTime;
                        lifeState = st.lifeState;
                        states.RemoveFirst();
                    }
                }
                else
                {
                    delete = true;
                }
            }
        }
        #endregion

        #region ExplosionState
        public class ExplosionState
        {
            public Vector2 pos;
            public int smokeTime;
            public LifeState lifeState;

            public ExplosionState() { }

            public ExplosionState(Vector2 Position, int SmokeTime, LifeState LifeState)
            {
                pos = Position;
                smokeTime = SmokeTime;
                lifeState = LifeState;
            }
        }
        #endregion

        #region Rock
        /// <summary>
        /// Definisce un singolo oggetto Rock.
        /// </summary>
        public class Rock
        {
            private LinkedList<RockState> states;
            public int deathCounter;
            public Vector2 pos;
            public bool delete;
            public int RockTime;
            public LifeState lifeState;
            public Vector2 direction;
            public Vector2 Position
            {
                get
                {
                    return pos;
                }
            }

            public LinkedList<RockState> GetStates()
            {
                return states;
            }
            public void SetStates(RockState[] newStates)
            {
                states = new LinkedList<RockState>(newStates);
            }

            public Rock() { }

            /// <summary>
            /// Istanzia un oggetto Rock con posizione Position
            /// </summary>
            /// <param name="Position">La posizione del fumo sullo schermo</param>
            public Rock(Vector2 Position, Vector2 Direction)
            {
                pos = Position;
                RockTime = 15;
                delete = false;
                lifeState = LifeState.NORMAL;
                states = new LinkedList<RockState>();
                deathCounter = 0;
                direction = Direction;
            }

            /// <summary>
            /// Aggiorna lo stato del fumo.
            /// </summary>
            public void Update()
            {
                if (lifeState != LifeState.DEAD)
                {
                    pos = pos + direction * 10;
                    if (RockTime < 0)
                    {
                        lifeState = LifeState.DEAD;
                    }
                    else
                    {
                        RockTime--;
                    }

                    states.AddFirst(new RockState(new Vector2(Position.X, Position.Y), RockTime, lifeState));
                    if (states.Count > gs.timeTank + 1)
                    {
                        states.RemoveLast();
                    }
                }
                else
                {
                    if (states.Count > 0)
                    {
                        if (states.Count + deathCounter > gs.timeTank)
                            states.RemoveLast();
                    }
                    else
                    {
                        delete = true;
                    }
                    deathCounter++;
                }
            }

            public void TimeTravel()
            {
                if (states.Count > 0)
                {
                    if (deathCounter > 0)
                        deathCounter--;
                    else
                    {
                        RockState st = states.First.Value;
                        pos = st.pos;
                        RockTime = st.smokeTime;
                        lifeState = st.lifeState;
                        states.RemoveFirst();
                    }
                }
                else
                {
                    delete = true;
                }
            }
        }
        #endregion

        #region RockState
        public class RockState
        {
            public Vector2 pos;
            public int smokeTime;
            public LifeState lifeState;

            public RockState() { }

            public RockState(Vector2 Position, int RockTime, LifeState LifeState)
            {
                pos = Position;
                smokeTime = RockTime;
                lifeState = LifeState;
            }
        }
        #endregion
    }
}